home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 40
/
Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso
/
Aminet
/
misc
/
emu
/
ATUtilities.lha
/
ATUtilities
/
M2
/
XMODEM.MOD
< prev
Wrap
Text File
|
2000-09-26
|
8KB
|
294 lines
IMPLEMENTATION MODULE XModem;
(* (C) Copyright 1987 Fitted Software Tools. All rights reserved.
This module is part of the example multitasking communications program
provided with the Fitted Software Tools' Modula-2 development system.
Registered users may use this program as is, or they may modify it to
suit their needs or as an exercise.
If you develop interesting derivatives of this program and would like
to share it with others, we encourage you to upload a copy to our BBS.
*)
FROM SYSTEM IMPORT ADR;
FROM System IMPORT Move;
FROM InOut IMPORT WriteString, WriteCard;
FROM Keyboard IMPORT KeyPressed, GetKeyCh;
FROM ASCII IMPORT SOH, ACK, NAK, EOT, CAN;
FROM RS232 IMPORT Init, GetCom, PutCom;
FROM Display IMPORT Goto;
FROM Windows IMPORT Window, OpenWindow, CloseCurWindow;
FROM LongJump IMPORT JumpBuffer, SetJump, LongJump;
FROM Files IMPORT Read, Write;
FROM Ticker IMPORT Ticks, OneSecond, TenSeconds, OneMinute;
CONST
commentLine = 0;
commentPos = 1;
statLine = 1;
statPos = 1;
errLine = 2;
errPos = 1;
BlockSize = 128;
BlockHigh = BlockSize - 1;
BlockFactor = 64;
VAR jumpBuff :JumpBuffer;
fileBuffer :ARRAY [0..BlockSize*BlockFactor-1] OF CHAR;
PROCEDURE SendFile( filename :ARRAY OF CHAR; fd :INTEGER; VAR ok :BOOLEAN );
VAR c :CHAR;
w :Window;
BEGIN
OpenWindow( w, 9,22, 13,75, TRUE, "XModem file transfer" );
Goto( commentLine, commentPos );
WriteString( "Sending file " ); WriteString( filename );
IF SetJump( jumpBuff ) = 0 THEN
Send( fd );
success( "File transfer terminated" );
END;
GetKeyCh( c );
CloseCurWindow;
END SendFile;
PROCEDURE ReceiveFile( filename :ARRAY OF CHAR; fd :INTEGER; VAR ok :BOOLEAN );
VAR c :CHAR;
w :Window;
BEGIN
OpenWindow( w, 9,22, 13,75, TRUE, "XModem file transfer" );
Goto( commentLine, commentPos );
WriteString( "Receiving file " ); WriteString( filename );
IF SetJump( jumpBuff ) = 0 THEN
Rcv( fd );
success( "File transfer terminated" );
END;
GetKeyCh(c);
CloseCurWindow;
END ReceiveFile;
PROCEDURE Send( fd :INTEGER );
VAR i, n :CARDINAL;
blockCount, sumck :CARDINAL;
errors :CARDINAL;
c, blk :CHAR;
ok :BOOLEAN;
buff :ARRAY [0..BlockHigh] OF CHAR;
PROCEDURE AbortXmit( msg :ARRAY OF CHAR );
BEGIN
error( msg );
LongJump( jumpBuff, 1 );
END AbortXmit;
PROCEDURE UpdtStatus;
BEGIN
Goto( statLine, statPos );
WriteString( "Blocks sent: " );
WriteCard( blockCount, 1 );
WriteString( ", Errors: " );
WriteCard( errors, 1 );
END UpdtStatus;
BEGIN
blockCount := 0; blk := 1C;
errors := 0;
LOOP
GetCh( c, OneMinute, ok );
IF NOT ok THEN AbortXmit( "no receiver" ) END;
IF c = CAN THEN AbortXmit( "cancelled by receiver" ) END;
IF c = NAK THEN EXIT END;
END;
LOOP
UpdtStatus;
Read( fd, ADR(buff), BlockSize, n );
IF n = 0 THEN EXIT END;
IF n < BlockSize THEN
WHILE n < BlockSize DO buff[n] := 0C; INC(n) END;
END;
LOOP
PutCom( SOH );
PutCom( blk ); PutCom( CHR(255 - ORD(blk)) );
sumck := 0;
FOR i := 0 TO BlockHigh DO
PutCom( buff[i] );
INC( sumck, ORD(buff[i]) );
END;
PutCom( CHR(sumck MOD 100H) );
GetCh( c, TenSeconds, ok );
IF NOT ok THEN AbortXmit( "timeout" ) END;
IF c = ACK THEN
INC( blockCount );
blk := CHR(blockCount+1);
EXIT;
(*
ELSIF c = CAN THEN AbortXmit( "cancelled by receiver" )
*)
ELSE
INC( errors );
END;
END;
END;
PutCom( EOT );
END Send;
PROCEDURE Rcv( fd :INTEGER );
VAR i :CARDINAL;
blk, blk1 :CHAR;
blockCount :CARDINAL;
lastblk, nextblk :CHAR;
sumck, sumck1 :CARDINAL;
timeouts, errors, retries :CARDINAL;
c :CHAR;
ok :BOOLEAN;
buff :ARRAY [0..BlockHigh] OF CHAR;
inBuffer :CARDINAL;
PROCEDURE AbortRcv( msg :ARRAY OF CHAR );
BEGIN
error( msg );
LongJump( jumpBuff, 1 );
END AbortRcv;
PROCEDURE WriteBuff( flush :BOOLEAN );
VAR n :CARDINAL;
BEGIN
Move( ADR(buff), ADR(fileBuffer[inBuffer*BlockSize]), BlockSize );
INC( inBuffer );
IF (inBuffer = BlockFactor) OR flush THEN
Write( fd, ADR(fileBuffer), inBuffer*BlockSize, n );
IF n <> inBuffer*BlockSize THEN
AbortRcv( "error writing to file" );
END;
inBuffer := 0;
END;
END WriteBuff;
PROCEDURE UpdtStatus;
BEGIN
Goto( statLine, statPos );
WriteString( "Blocks received: " );
WriteCard( blockCount, 1 );
WriteString( ", Errors: " );
WriteCard( errors+retries, 1 );
END UpdtStatus;
BEGIN
inBuffer := 0;
blockCount := 0; lastblk := 0C; nextblk := 1C;
errors := 0; retries := 0;
PutCom( NAK );
LOOP
UpdtStatus;
timeouts := 0;
LOOP
GetCh( c, TenSeconds, ok );
IF ok THEN
IF c = SOH THEN EXIT END;
IF c = EOT THEN
WriteBuff( TRUE );
PutCom( ACK );
RETURN;
END;
ELSE
IF timeouts > 6 THEN AbortRcv( "timeout" ) END;
FlushInput;
PutCom( NAK );
INC( timeouts );
END;
END;
GetCh( blk, OneSecond, ok );
IF NOT ok THEN AbortRcv( "timeout" ) END;
GetCh( blk1, OneSecond, ok );
IF NOT ok THEN AbortRcv( "timeout" ) END;
i := 0;
LOOP
GetCh( buff[i], OneSecond, ok );
IF ok THEN INC( i )
ELSE EXIT END;
IF i >= BlockSize THEN EXIT END;
END;
GetCh( c, OneSecond, ok );
sumck := ORD( c );
INC( retries );
IF NOT ok OR (blk <> CHR(255-ORD(blk1))) OR (i < BlockSize) THEN
(* bad or incomplete block *)
FlushInput;
PutCom( NAK );
ELSIF blk = lastblk THEN
(* resent previous block *)
PutCom( ACK );
INC( errors, retries-1 ); retries := 0;
ELSIF blk = nextblk THEN
sumck1 := 0;
FOR i := 0 TO BlockHigh DO INC( sumck1, ORD(buff[i]) ) END;
IF sumck1 MOD 100H = sumck THEN
WriteBuff( FALSE );
PutCom( ACK );
INC( errors, retries-1 ); retries := 0;
lastblk := nextblk;
INC( blockCount );
nextblk := CHR( (blockCount+1) MOD 100H );
ELSE
FlushInput;
PutCom( NAK );
END;
ELSE
FlushInput;
PutCom( NAK );
END;
IF retries >= 10 THEN AbortRcv( "too many retries" ) END;
END;
END Rcv;
PROCEDURE FlushInput;
VAR c :CHAR;
input :BOOLEAN;
BEGIN
REPEAT
GetCh( c, 2, input ); (* timeout 50-100ms *)
UNTIL NOT input;
END FlushInput;
(*
This COM input routine does not suspend on RS232Signal as we need to
timeout and the Kernel does not provide that facility.
*)
PROCEDURE GetCh( VAR c :CHAR; timeout :CARDINAL; VAR input :BOOLEAN );
VAR ticks :CARDINAL;
BEGIN
ticks := Ticks;
LOOP
GetCom( c, input );
IF input THEN RETURN END;
IF Ticks - ticks > timeout THEN RETURN END;
END;
END GetCh;
PROCEDURE error( msg :ARRAY OF CHAR );
BEGIN
Goto( errLine, errPos );
WriteString( "--- " ); WriteString( msg ); WriteString( " --- " );
END error;
PROCEDURE success( msg :ARRAY OF CHAR );
BEGIN
Goto( errLine, errPos );
WriteString( "+++ " ); WriteString( msg ); WriteString( " +++ " );
END success;
END XModem.